package sudoku;


import fr.jussieu.script.*;
import java.util.Stack;
import java.util.Vector;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Scanner;
import java.util.Formatter;


/**
 * Contient une grille et les methodes 
 * de travail pour le sudoku (generation, resolution, etc...)
 * 
 
 */
public class GrilleSudoku {
	public int[][] grille;
	public int tailleGrille;
	public int solution[][];
	public int sep_l;
	public int sep_c;
	public int nbSolutionsMax=300;
	public int nbCasesVides;
	public int nbCasesImm;
	public int nbCandUniques;
	public int nbDoublets;
	public int difficulte;
	public int nbSolutions;
	public int orientation;
	public boolean[][] positionEnoncer;
	public boolean verbose;
	public boolean explicite = false;
	static public int difficulteMax=4;
	public int[][][] solutionsTmp;
	public int[][][] lastTabCand;
	public int nbCasesVidesTmp;
	public int nbCasesImmTmp;
	public int nbCandUniquesTmp;
	public int nbDoubletsTmp;
	Chronometre chrono;
	
	public static void main(String[] args) {
//		GrilleSudoku grille = new GrilleSudoku(9, 'v', 0, false);
//		grille.afficher(grille.generateur(60));
//		try {
//			grille.charger("/home/pl/test", true);
//		}
//		catch (IOException e){
//			System.out.println("Erreur : "+e.getMessage());
//		}
//		catch (ExceptionsSudoku f){
//			System.out.println("Erreur : "+f.getMessage());
//		}
//		System.out.println(grille.estJuste(grille.grille));
//		System.out.println(grille.resoudre(grille.grille, false, true)[0][0][0]);
//		System.out.println(grille.generateur(50)[0]);
	}

	/**
	 * Par defaut genere une grille de taille 9 et de difficulte 2
	 */
	GrilleSudoku() {
		this(10,'v',0, true);
	}

	/**
	 * Cree une nouvelle grille de travail (vide ou generee)
	 *  
	 * @param l taille de la grille
	 * @param orientat orientation de la grille (v(erticale) ou h(orizontale))
	 * @param difficulte difficulte de la grille a generer, si 0, pas de generation
	 * @param verb mode verbose
	 */
	GrilleSudoku(int l, char orientat, int difficulte, boolean verb) {
		verbose = verb;
		orientation = orientat;
		grille = new int[l][l];
		if (l>35){
			l = 35;
		}
		tailleGrille = l;
		/* On definit la taille des block
		 */
		this.setTailleBlock();
		positionEnoncer= new boolean[tailleGrille][tailleGrille];
		/* On remplie le tableau de valeurs vides */
		for (int i=0; i<tailleGrille; i++) {
			for (int j=0; j<tailleGrille; j++) {
				grille[i][j] = -1;
			}
		}
		/* On genere une nouvelle grille si demande */
		/* difficulte=0 => pas de generation */
		if (difficulte>difficulteMax){
			this.generer(difficulteMax, true);
		} else if (difficulte>0){
			this.generer(difficulte, true);
		}

	}
	
	
	/**
	 * Calcul la dimension des block de la grille
	 * et les definit pour la grille courante.
	 */
	public void setTailleBlock(){
		/* On definit l'orientation de la grille */
		if (orientation!='v' && orientation!='h') {
			orientation = 'v';
		}
		Integer length = new Integer(tailleGrille);
		double sqrt = Math.sqrt(length.doubleValue());
		Double lengt = new Double(sqrt);
		if (sqrt-Math.floor(sqrt)==0) {
			sep_l = lengt.intValue();
			sep_c = lengt.intValue();
		} else {
			for (int i=lengt.intValue(); i>0; i--) {
				if (tailleGrille%i==0) {
					if (orientation=='v') {
						sep_l = Math.max(i, tailleGrille/i);
						sep_c = Math.min(i, tailleGrille/i);
					} else {
						sep_c = Math.max(i, tailleGrille/i);
						sep_l = Math.min(i, tailleGrille/i);
					}
					break;
				}
			}
		}
		if (verbose) System.out.println("Taille des blocks : ["+sep_l+","+sep_c+"]");
	}

	/**
	 * Modifie une valeur dans la grille..
	 * Possibilite de verifier si le nombre entre
	 * n'entre pas en conflit avec un autre nombre.
	 * 
	 * @param l ligne
	 * @param c colonne
	 * @param valeur valeur a integrer
	 * @param check on verifie ou pas les conflits
	 * @return true si la valeur modifiee est OK avec verification des conflits ou non,
	 * false si la valeur modifier entre en conflit
	 */
	public boolean modifierValeur(int l, int c, int valeur, boolean check) {
		int[][] tabl = new int[tailleGrille][tailleGrille];
		if (check){
			tabl = this.copieTabl(grille);
			tabl[l][c] = valeur;
			if (this.estJuste(tabl)){
				grille=tabl;
				if (verbose) System.out.println("Case ["+(l+1)+","+(c+1)+"] modifiee en "+this.convertir(valeur));
				return true;
			} else {
				if (verbose) System.out.println("Erreur : la grille n'est plus juste\nAucune case modifiee.");
				return false;
			}
		} else {
			grille[l][c] = valeur;	
			if (verbose && valeur!=-1) System.out.println("Case ["+(l+1)+","+(c+1)+"] modifiee en "+this.convertir(valeur));
			return true;
		}
	}

	/**
	 * Genere une nouvelle grille de la difficutle indiquee
	 * 
	 * @param difficulte difficulte de la grille a generer
	 * @param grilleCourante indique si on definit la grille 
	 * generee en tant que grille principale
	 * 
	 * @return la grille generee
	 */
	public int[][] generer(int difficulte, boolean grilleCourante){
		int nbCasesvVides = 0;
		int[][] grilleTmp = new int[tailleGrille][tailleGrille];

		if (difficulte>difficulteMax){
			difficulte = difficulteMax;
		}
		if (tailleGrille < 6 && difficulte > 1 ) difficulte = 1;
		if (tailleGrille < 8 && difficulte > 3 ) difficulte = 3;
		if (verbose) System.out.print("\n\nGeneration de la grille en cours...");
		if (tailleGrille<10){
			switch (difficulte){
			case 1 :
				nbCasesvVides = (int)(tailleGrille*tailleGrille*62/100);
				break;
			case 2 :
				nbCasesvVides = (int)(tailleGrille*tailleGrille*65/100);
				break;
			case 3 :
				nbCasesvVides = (int)(tailleGrille*tailleGrille*65/100);
				break;
			default :
				nbCasesvVides = (int)(tailleGrille*tailleGrille*68/100);
			break;
			}
		} else if (tailleGrille<15) {
			switch (difficulte){
			case 1 :
				nbCasesvVides = (int)(tailleGrille*tailleGrille*55/100);
				break;
			case 2 :
				nbCasesvVides = (int)(tailleGrille*tailleGrille*60/100);
				break;
			case 3 :
				nbCasesvVides = (int)(tailleGrille*tailleGrille*60/100);
				break;
			default :
				nbCasesvVides = (int)(tailleGrille*tailleGrille*62/100);
			break;
			}
		} else if (tailleGrille<20){
			switch (difficulte){
			case 1 :
				nbCasesvVides = (int)(tailleGrille*tailleGrille*45/100);
				break;
			case 2 :
				nbCasesvVides = (int)(tailleGrille*tailleGrille*45/100);
				break;
			default :
				nbCasesvVides = (int)(tailleGrille*tailleGrille*45/100);
			break;
			}
		} else {
			nbCasesvVides = (int)(tailleGrille*tailleGrille*35/100);
		}
		grilleTmp = this.generateur(nbCasesvVides);
		while (this.evaluerDifficulte(grilleTmp, verbose)!=difficulte){
			grilleTmp = this.generateur(nbCasesvVides);
		}
		nbCasesVides = nbCasesvVides;
		if (verbose) {
			System.out.println("OK"); 
		}
		if (grilleCourante){
			positionEnoncer= new boolean[tailleGrille][tailleGrille];
			for (int i=0; i<tailleGrille; i++){
				for (int j=0; j<tailleGrille; j++){
					if (grilleTmp[i][j]!=-1){
						positionEnoncer[i][j] = true;
					}
				}
			}
			this.setGrilleCourante(grilleTmp);
		}
		if (verbose) {
			this.afficher(grilleTmp);
			System.out.println("\nDifficulte : "+difficulte
					+"\nNombre de cases vides : "+nbCasesVidesTmp
					+"\nNombre de cases Immediates : "+nbCasesImmTmp
					+"\nNombre de candidats uniques : "+nbCandUniquesTmp
					+"\nNombre de doublets : "+nbDoubletsTmp);
			if (nbCasesVidesTmp==nbCasesImmTmp+nbCandUniquesTmp+nbDoubletsTmp){
				System.out.println("La grille est resolue !\n");
			}
		}

		return grilleTmp;
	}

	/** 
	 * Affiche une grille formatee dans la console.
	 * 
	 * @param grille le tableau a afficher
	 */
	public void afficher(int[][] grille) {
		String aff = "";
		String sep = "";
		int cpt;
		for (int i=0; i<tailleGrille; i++) {
			cpt=0;
			aff = aff+"  ";
			for (int j=0; j<tailleGrille; j++) {
				if (j%this.sep_c==0) {
					aff=aff+"| ";
					cpt++;
				}
				aff = aff+this.convertir(grille[i][j])+" ";
			}
			aff = aff+"|\n";
			if ((i+1)%sep_l==0) {
				sep = "";
				for (int c=0; c<tailleGrille+cpt+1; c++) {
					if (c%(sep_c+1)==0){
						sep = sep+"+ ";
					} else {
						sep = sep+"- ";
					}
				}
				aff = aff+"  "+sep+"\n";
			}
		}
		aff = "\n  "+sep+"\n"+aff;
		System.out.println(aff);
	}

	/**
	 * Convertit une valeur "humaine" en valeur
	 * "numerique" pour la grille.
	 * 
	 * @param val charactere a convertir
	 * @return charactere formate pour la grille
	 */
	public int convertir(char val) {
		if (Character.isDigit(val) && (int)(val)!=0) {
			return (int) (val-48);
		} else {
			if (val=='0' || val=='?' || val==' ' ) {
				return -1;
			} else {
				char tmp = 'A';
				int cpt = 0;
				while (tmp!=val) {
					tmp = (char)(tmp + 1);
					cpt++;
				}
				return (10+cpt);
			}
		}
	}

	/**
	 * Convertit une valeur "numerique" en valeur
	 * "humaine" pour la lecture.
	 * 
	 * @param val valeur dans la grille
	 * @return valeur a afficher
	 */
	public String convertir(int val) {
		if (val<10) {
			if (val==-1) {
				return " ";
			} else {
				return Deug.intToString(val);
			}
		} else {
			return Deug.charToString((char) ('A'+val-10));
		}
	}

	/**
	 * Permet de genere une grille a solution unique 
	 * de maniere aleatoire.
	 * On remplit un bloc avec des valeurs aleatoires
	 * puis on backtrack la grille pour obtenir une grille juste.
	 * On eneleve ensuite des cases au hasard tout en verifiant
	 * si la grille possede toujours une solution unique.
	 * 
	 * @return grille generee
	 */
	public int[][] generateur(int nbCasesVides){
		int[][] grille = new int[tailleGrille][tailleGrille];
		for (int i=0; i<tailleGrille; i++) {
			for (int j=0; j<tailleGrille; j++) {
				grille[i][j] = -1;
			}
		}
		boolean[] val = new boolean[tailleGrille];
		int nbTmp;
		int haut = tailleGrille;

		/* On peuple un premier block (au hasard) avec des
		 * valeurs aleatoires
		 */
		int l = randNb(0,sep_l-1);
		int c = randNb(0,sep_c-1);
		for (int i=l*sep_l; i<l*sep_l+sep_l; i++) {
			for (int j=c*sep_c; j<c*sep_c+sep_c; j++) {
				nbTmp = this.randNb(1, haut);
				while (val[nbTmp-1]) {
					nbTmp = this.randNb(1, haut);
				}
				val[nbTmp-1] = true;
				grille[i][j] = nbTmp;
			}
		}
		/* On resoud la grille pour avoir une grille complete
		 */
		grille=this.resoudre(grille, explicite, true)[1];
		/*On enleve le nombre de cases necessaires
		 * en s'assurant que la grille possede toujours
		 * une solution unique
		 */
		

		// on rearrange les nombres pour annuler l'effet "generer par ordinateur"
		int[] dealer = new int[tailleGrille];
		boolean[] check = new boolean[tailleGrille];
		int nb;
		int cpt=0;
		for (int i=0;i<tailleGrille;i++){
			while (check[(nb = randNb(1,tailleGrille))-1]){
				nb = randNb(1,tailleGrille);
			}
			check[nb-1]=true;
			dealer[cpt++] = nb;
		}
		for (int i=0;i<tailleGrille;i++){
			for (int j=0;j<tailleGrille;j++){
				grille[i][j] = dealer[grille[i][j]-1];
			}
		}
		cpt=0;
		int cptEssais = 1;
		int i,j;
		int[][] grilleTmp;
		int[][] lastVal = new int[nbCasesVides+1][3];
		while (cpt<nbCasesVides){
			i = this.randNb(0,tailleGrille-1);
			j = this.randNb(0,tailleGrille-1);
			while (grille[i][j]==-1){
				i = this.randNb(0,tailleGrille-1);
				j = this.randNb(0,tailleGrille-1);
			}	
			grilleTmp=this.copieTabl(grille);
			grilleTmp[i][j]=-1;
			// Impossible de trouver une solution unique, 
			// on recommence a zero
			if (cptEssais>tailleGrille*tailleGrille){
				for (int x=0;x<cpt;x++){
					grille[lastVal[x][1]][lastVal[x][2]] = lastVal[x][0];
				}
				cptEssais = 0;
				cpt=0;
			}
			if (this.resoudre(grilleTmp,true, true)[0][0][0]==1){
				lastVal[cpt][0] = grille[i][j];
				lastVal[cpt][1] = i;
				lastVal[cpt][2] = j;
				grille[i][j] = -1;
				cpt++;
				cptEssais = 1;
			}
			cptEssais++;
		}
		return grille;

	}

	/**
	 * Resoud une grille
	 * 
	 * @param grille le tableau a resoudre
	 * @param explicite true pour utiliser le backtrack explicite, sinon on utilise le backtrack explicite 
	 * @param unique indique si on ne recherche que des solutions uniques
	 * @return tableau avec en [0][0][0] le nombre de solution et en [i>0][x][y] les differentes solutions
	 */
	public int[][][] resoudre(int[][] grille, boolean explicite, boolean unique){
		int nbSolutions = nbSolutionsMax;
		if (unique){
			nbSolutions = 2;
		}
		int[][][] resultat = new int[nbSolutions][tailleGrille][tailleGrille];		
		if (this.estJuste(grille)){
		
		boolean flag = false;
		int[][] grilleTmp = this.copieTabl(grille);

		this.casesImmediates(grilleTmp);
		for (int i=0; i<tailleGrille; i++){
			for (int j=0; j<tailleGrille; j++){
				if (grilleTmp[i][j]==-1){
					flag =true;
				}	
			}
		}
		if(flag){
			/* On effectue le backtrack de resolution de la grille */
			if (explicite){
				this.backtrackExpl(grilleTmp, unique);
				resultat = solutionsTmp;
			} else {
				this.backtrackrec(0, 0, grilleTmp, unique);
				resultat = solutionsTmp;
			}
		} else {
			/* La grille a une solution, pas besoin de backtrack */
			resultat[0][0][0]=1;
			resultat[1] = this.copieTabl(grilleTmp);
		}
		} else {
			resultat[0][0][0]=0;
		}
		return resultat;
	}

	/**
	 * Genere un nombre aleatoire
	 * dans un intervalle donne.
	 * 
	 * @param bas borne inferieure de l'intervalle
	 * @param haut borne superieure de l'intervalle
	 * @return nombre genere;
	 */
	public int randNb(int bas, int haut) {
		haut = haut+1;
		return (int) (Math.random()*(haut-bas))+bas;
	}

	/**
	 * Copie un tableau a 2 dimensions dans une autre
	 * variable pour eviter le passage par reference.
	 * 
	 * @param tab tableau a copier
	 * @return le tableau copie
	 */
	public int[][] copieTabl(int[][] tab) {
		int[][] tabl = new int[tailleGrille][tailleGrille];
		for (int i=0; i<tailleGrille; i++) {
			System.arraycopy(tab[i], 0, tabl[i], 0, tailleGrille);
		}
		return tabl;
	}

	/**
	 * Trouve et place les cases immediates dans une grille
	 * 
	 * @param grille la grille a resouudre
	 * @return nombre de cases immediates
	 */
	public int casesImmediates(int[][] grille){
		
		boolean flag=true;
		int cpt=0;
		int cp=0;
		int[] valPoss;
		int val=0;
		/* On remplit les cases remplissables immediatement
		 */
		while(flag){
			flag=false;
			for (int i=0; i<tailleGrille;i++){
				for (int j=0; j<tailleGrille;j++){
					if (grille[i][j]==-1){
						cp=0;
						valPoss = this.valeursPossibles(i, j, grille);
						for (int x=0;x<tailleGrille;x++){
							if (valPoss[x]==0){
								val=x+1;
								cp++;
							}
						}
						if (cp==1){
							grille[i][j]=val;
							cpt++;
							flag=true;
						}
					}
				}
			}
		}
		return cpt;
	}

	/**
	 * Resoud une grille par backtrack recursif
	 * 
	 * @param i indice de ligne
	 * @param j indice de colonne
	 * @param gril la grille a resoudre a resoudre
	 * @param unique indique si on ne doit pas chercher plus de deux solutions
	 * pour la generation de grilles a solution unique)
	 * comme solution de la grille courante
	 */
	public void backtrackrec(int i, int j, int[][] grille, boolean unique) {
		int[] rs;
		int nbSolutions = nbSolutionsMax;
		if (unique){
			nbSolutions = 2;
		}
		if (i==0 && j==0){
			solutionsTmp = new int[nbSolutionsMax+1][tailleGrille][tailleGrille];
			solutionsTmp[0][0][0]=0;
		}
		
		if (solutionsTmp[0][0][0]<nbSolutions){
			if (grille[i][j] == -1) {
				/* Pour chaque valeur possible, on backtrack
				 */
				int cpt = 0;
				int[] valPoss = this.valeursPossibles(i,j,grille);
				for (int f = 0; f<tailleGrille; f++) {
					if (valPoss[f] == 0) {
						grille[i][j] = f+1;
						rs = this.rangSuivant(i, j);
						backtrackrec(rs[0], rs[1], grille, unique);
						cpt++;
					}
				}
				grille[i][j] = -1;

			} else {
				if (((i==tailleGrille-1) && (j==tailleGrille-1))) {
					/* Le tableau est solution, on le pose comme solution */
					solutionsTmp[0][0][0]++;
					solutionsTmp[solutionsTmp[0][0][0]] = this.copieTabl(grille);
					return;
				} else {
					rs = this.rangSuivant(i, j);
					backtrackrec(rs[0], rs[1], grille, unique);
				}
			}
		}
	}

	/**
	 * Trouve les indices suivants dans une grille.
	 * Utilise pour backtrack recursif.
	 * 
	 * @param i ligne courante
	 * @param j colonne courante
	 * @return tableau contenant les indices du rang suivant
	 */
	public int[] rangSuivant(int i, int j) {
		int[] tmp = new int[2];
		if (i == tailleGrille-1 && j==tailleGrille-1) {
			tmp[0] = i;
			tmp[1] = j;
		} else if (j==tailleGrille-1) {
			tmp[0] = i+1;
			tmp[1] = 0;
		} else {
			tmp[0] = i;
			tmp[1] = j+1;
		}
		return tmp;
	}
	
	/**
	 * Resoud une grille par backtrack explicite.
	 * 
	 * @param gril la grille a resoudre
	 * @param unique indique si on ne doit rechercher que des solution uniques
	 * (on s'arrete apres avoir trouve deux solutions)
	 */
	public void backtrackExpl(int[][] gril, boolean unique){
		int[][] grille = this.copieTabl(gril);
		Position tp;
		int nbSolutions = nbSolutionsMax;
		if (unique){
			nbSolutions = 2;
		}
		solutionsTmp = new int[nbSolutionsMax+1][tailleGrille][tailleGrille];
		solutionsTmp[0][0][0]=0;
		Stack<Integer> solu=new Stack<Integer>();
		@SuppressWarnings("unchecked")
		Vector<Integer>[][] tab_sol= (Vector<Integer>[][]) new Vector[tailleGrille][tailleGrille];
		int i,j;
		int x;
		int cpt=0;
		for(int a=0; a<tailleGrille; a++){
			for(int b=0; b<tailleGrille; b++){
				if(grille[a][b]==-1){
					cpt++;
				}
			}
		}
		if (cpt>0){
			Position[] positions=new Position[cpt];
			cpt=0;
			for (int a=0; a<tailleGrille; a++){
				for (int b=0; b<tailleGrille; b++){
					if(grille[a][b]==-1){
						positions[cpt]=new Position(a,b);
						cpt++;
					}
				}
			}
			int rang=0;
			tp = positions[rang];
			i = tp.lig;  j= tp.col;
			int[] valPoss = new int[tailleGrille]; 
			valPoss = this.valeursPossibles(i,j,grille);
			tab_sol[i][j]=new Vector<Integer>();
			for (int g=0; g<tailleGrille; g++){
				if(valPoss[g]==0){
					tab_sol[i][j].add(new Integer(g+1));
				}
			}
			while(rang>=0){
				tp = positions[rang];
				i = tp.lig; j=tp.col;	
				while(tab_sol[i][j].size()>0 && solutionsTmp[0][0][0]<=nbSolutions){
					x = ((Integer)tab_sol[i][j].get(0)).intValue();
					solu.push(new Integer(x));
					grille[i][j] = x;
					tab_sol[i][j].removeElementAt(0);
					if(solu.size()==positions.length){
						solutionsTmp[0][0][0]++;
						solutionsTmp[solutionsTmp[0][0][0]] = this.copieTabl(grille);
						solu.pop();
						grille[i][j]=-1;
						if(rang!=0){
							rang--;
							tp = positions[rang];
							i=tp.lig;j=tp.col;
							solu.pop();
						}

					} else {
						rang++;
						tp = positions[rang];
						i=tp.lig;j=tp.col;
						valPoss = this.valeursPossibles(i,j,grille);
						tab_sol[i][j]=new Vector<Integer>();
						for (int g=0;g<tailleGrille;g++){
							if(valPoss[g]==0){
								tab_sol[i][j].add(new Integer(g+1));
							}	
						}
					}
				}
				if(!solu.empty()){
					solu.pop();
				}
				grille[i][j]=-1;
				rang--;
			}
		} else {
			solutionsTmp[0][0][0]=1;
		}
	}

	/**
	 * Classe definissant une position par ses coordonnees.
	 */
	public class Position {
		int lig, col;

		/**
		 * Constructeur de la classe
		 * 
		 * @param i ligne
		 * @param j colonne
		 */
		Position(int i, int j) {
			lig = i; col = j;
		}
	}

	/**
	 * Determine les valeurs possibles d'une case vide.
	 * Renvoie un tableau contenant le nombre d'occurrences 
	 * d'un nombre dans ligne, colonne et bloc correspondants<br>.
	 * indice_tableau +1 => valeurs<br>
	 * tab[indice] => nombre d'occurence du nombre indice+1 
	 * 
	 * @param i indice de ligne
	 * @param j indice de colonne
	 * @param grille tableau a analyser 
	 * @return tableau des valeurs
	 */
	public int[] valeursPossibles(int i, int j, int[][] grille){
		int[] valPossibles = new int[tailleGrille];
		int lig = i/sep_l;
		int col = j/sep_c;
		/* on verifie ligne et colonne correspondantes  
		 */
		for (int x=0; x<tailleGrille; x++) {
			if (j!=x && grille[i][x]!=-1) {
				valPossibles[grille[i][x]-1]++;
			}
			if (i!=x && grille[x][j]!=-1) {
				valPossibles[grille[x][j]-1]++;
			}
		}
		/* on verifie le block correspondant */
		for (int x = lig*sep_l; x<lig*sep_l+sep_l; x++) {
			for (int y = col*sep_c; y<col*sep_c+sep_c; y++) {
				if (x!=i && y!=j && grille[x][y]!=-1) {
					valPossibles[grille[x][y]-1]++;
				}
			}
		}
		return valPossibles;
	}

	/**
	 * Evalue la difficulte d'une grille
	 * 
	 * @param gril la grille a evaluer
	 * @param verbose indique si la fonction doit detailler son fonctionnement
	 * @return la difficulte
	 */
	public int evaluerDifficulte(int[][] gril, boolean verbose){
		if (verbose) System.out.println("\nEvaluation de la difficulte en cours...");
		int[][] grille = this.copieTabl(gril);
		nbCasesImmTmp = 0;
		nbCandUniquesTmp = 0;
		nbDoubletsTmp = 0;
		nbCasesVidesTmp = 0;

		// Calcul du nombre de cases vides
		for (int i=0; i<tailleGrille; i++){
			for (int j=0; j<tailleGrille; j++){
				if (grille[i][j]==-1){
					nbCasesVidesTmp++;
				}
			}
		}
		// Creation du tableau contenant les candidats
		int[][][] tab_glob=this.grilleCandidats(grille);
		this.evaluationTechniques(tab_glob, true, verbose);
		if (nbDoubletsTmp!=0){
				difficulte=4;
		} else if (nbCandUniquesTmp!=0){
			if (nbCasesImmTmp<nbCandUniquesTmp){
				difficulte=3;
			} else {
				difficulte=2;
			}
		} else {
			difficulte=1;
		}
		return difficulte;
	}

	/**
	 * Fait se succeder les differentes methodes humaines
	 * de resolution le plus loin possible
	 * 
	 * @param grilleCand la grille avec ses candidats
	 * @param first indique si premiere fois pour la grille
	 * @param verbose indique si la fonction doit detailler son fonctionnement
	 */
	public void evaluationTechniques(int[][][] grilleCand, boolean first, boolean verbose){
		int tmp_nb_casesimm=0;
		int tmp_nb_candidatsuniques=0;
		int tmp_nb_doublets=0;

		if (first){
			lastTabCand = new int[tailleGrille][tailleGrille][tailleGrille];
			for (int i=0; i<tailleGrille; i++){
				for (int j=0; j<tailleGrille;j++){
					for (int x=0; x<tailleGrille; x++){
						lastTabCand[i][j][x]=-2;

					}
				}
			}
		}
		tmp_nb_casesimm = this.caseImmediate(grilleCand, verbose);
		tmp_nb_candidatsuniques=this.candidatUnique(grilleCand, verbose);
		if (tmp_nb_casesimm+tmp_nb_candidatsuniques>0){
			lastTabCand = this.copieTabl(grilleCand);
			nbCasesImmTmp= nbCasesImmTmp + tmp_nb_casesimm;
			nbCandUniquesTmp = + nbCandUniquesTmp + tmp_nb_candidatsuniques;
			this.evaluationTechniques(grilleCand, false, verbose);	
		} else {
			tmp_nb_doublets=this.candidatsIdentiques(grilleCand);
			if (tmp_nb_doublets>0 && !this.comparerTab(lastTabCand, grilleCand)){
				lastTabCand = this.copieTabl(grilleCand);
				nbCasesImmTmp= nbCasesImmTmp + tmp_nb_casesimm;
				nbCandUniquesTmp = + nbCandUniquesTmp + tmp_nb_candidatsuniques;
				nbDoubletsTmp = + nbDoubletsTmp + tmp_nb_doublets;
				this.evaluationTechniques(grilleCand, false, verbose);	
			}
		}
	}

	/**
	 * Copie un tableau a 3 dimensions dans une autre
	 * variable pour eviter le passage par reference.
	 * 
	 * @param tab tableau a copier
	 * @return le tableau copie
	 */
	public int[][][] copieTabl(int[][][] tab) {
		int[][][] tabl = new int[tailleGrille][tailleGrille][tailleGrille+1];
		for (int i=0; i<tailleGrille; i++) {
			for (int j=0; j<tailleGrille; j++) {
				System.arraycopy(tab[i][j], 0, tabl[i][j], 0, tailleGrille+1);
			}
		}
		return tabl;
	}

	/**
	 * Compare 2 tableau a 3 dimensions
	 * 
	 * @param tab1 premier tableau a comparer
	 * @param tab2 second tableau a comparer
	 * @return true si les tqblequx sont identiques, false sinon
	 */
	public boolean comparerTab(int[][][] tab1, int[][][] tab2){
		boolean identiques = true;
		for (int i=0; i<tailleGrille; i++) {
			for (int j=0; j<tailleGrille; j++) {
				for (int x=0; x<tailleGrille; x++){
					if(tab1[i][j][x]!=tab2[i][j][x]){
						identiques = false;
					}
				}
			}
		}
		return identiques;
	}

	/**
	 * Cree une grille contenant les candidats 
	 * pour chaque case
	 * 
	 * @param grille la grille dont on veut les candidats
	 * @return la grille avec ses candidats
	 */
	public int[][][] grilleCandidats(int[][] grille){
		int[][][] grilleCand = new int[tailleGrille][tailleGrille][tailleGrille+1];
		int[] tmp_tab;
		int a=0;
		int cpt=0;
		for (int i=0;i<tailleGrille;i++){
			for (int j=0;j<tailleGrille;j++){
				if (grille[i][j]==-1){
					cpt=0;
					tmp_tab = this.valeursPossibles(i,j,grille);
					for (int x=0;x<tailleGrille;x++){
						if (tmp_tab[x]==0){
							grilleCand[i][j][cpt]=x+1;
							cpt++;
						}
					}
					grilleCand[i][j][tailleGrille]=cpt;
					if (cpt==1) a++;;
				} else {
					grilleCand[i][j][tailleGrille]=0;
				}
			}
		}
		return grilleCand;
	}

	/** 
	 * Remplie les cases immediatement 
	 * remplissables d'une grille a trous
	 * 
	 * @param grilleCand le tableau a remplir
	 */
	public int caseImmediate(int[][][] grilleCand, boolean verbose){
		boolean flag = true;
		int cpt = 0;
		int  x;
		while(flag){
			flag = false;
			for (int i=0; i<tailleGrille; i++){
				for (int j=0; j<tailleGrille; j++){
					if (grilleCand[i][j][tailleGrille]==1){
						x = 0;
						for (int w=0; w<tailleGrille; w++){
							if (grilleCand[i][j][w]!=0){
								x = w;
							}
						}
						if (verbose) System.out.println("Cases immediate trouvee : ["+(i+1)+","+(j+1)+"] "+this.convertir(grilleCand[i][j][x]));
						this.grilleCandUpdate(i, j, grilleCand[i][j][x], grilleCand);
						cpt++;
						flag=true;
					}
				}
			}
		}
		return cpt;
	}

	/**
	 * Met a jour une grille de candidats
	 * 
	 * @param i indice de ligne
	 * @param j indice de colonne
	 * @param candidat candidat a enlever
	 * @param grilleCand la grille de candidats a mettre a jour
	 */
	public void grilleCandUpdate(int i, int j, int candidat, int[][][] grilleCand){

		for (int x=0;x<tailleGrille;x++){
			if (grilleCand[i][j][x]==candidat){
				grilleCand[i][j][x]=0;
				grilleCand[i][j][tailleGrille]--;
			}
		}
		// Update ligne && colonne
		for (int x=0;x<tailleGrille;x++){
			if (grilleCand[x][j][tailleGrille]>0 && x!=i){
				for (int y=0;y<tailleGrille;y++){
					if (grilleCand[x][j][y]==candidat){						
						grilleCand[x][j][y]=0;
						grilleCand[x][j][tailleGrille]--;
					}
				}
			}
			if (grilleCand[i][x][tailleGrille]>0 && x!=j){
				for (int y=0;y<tailleGrille;y++){
					if (grilleCand[i][x][y]==candidat){
						grilleCand[i][x][y]=0;		
						grilleCand[i][x][tailleGrille]--;
					}
				}
			}
		}
		// Update block
		int lig=i/sep_l;
		int col=j/sep_c;
		for (int x=lig*sep_l; x<lig*sep_l+sep_l; x++){
			for (int y=col*sep_c; y<col*sep_c+sep_c; y++){
				if (x!=i && y!=j){
					if (grilleCand[x][y][tailleGrille]>0){
						for (int a=0; a<tailleGrille; a++){
							if (grilleCand[x][y][a]==candidat){
								grilleCand[x][y][a]=0;		
								grilleCand[x][y][tailleGrille]--;
							}
						}
					}
				}
			}
		}	
	}

	/**
	 * Trouve les doublets identiques d'une grille
	 * 
	 * @param grilleCand la grille de candidats
	 * @return le nombre de doublets trouves
	 */
	public int candidatsIdentiques(int[][][] grilleCand){
		int identiques=2;
		int[] tmp_doublets=new int[identiques];
		int[][] doublets=new int[tailleGrille][identiques];
		int cpt=0;
		int cpt_l=0;
		int nb_doublets=0;
		int nbDoublets=0;

		for (int i=0; i<tailleGrille; i++){
			cpt=0;
			// Lignes
			cpt=0;
			doublets=new int[tailleGrille][identiques];
			for (int j=0; j<tailleGrille; j++){
				cpt_l=0;
				if (grilleCand[i][j][tailleGrille]==identiques){
					for (int x=0; x<tailleGrille; x++){
						if (grilleCand[i][j][x]>0){
							doublets[cpt][cpt_l]=grilleCand[i][j][x];
							cpt_l++;
						}
					}	
					cpt++;
				}

			}
			nb_doublets=cpt;
			cpt=0;
			// Identification doublets
			if (nb_doublets>=identiques){
				tmp_doublets=this.idIdentiques(doublets, identiques);
				if (tmp_doublets[0]!=-1){
					nbDoublets++;
					if (verbose) System.out.println("Doublet trouve : ("+tmp_doublets[0]+","+tmp_doublets[1]+")");
					// On a trouver un doublets
					for (int j=0; j<tailleGrille ;j++){
						if (grilleCand[i][j][tailleGrille]!=identiques){
							for (int x=0; x<identiques; x++){
								this.grilleCandUpdate(i, j, tmp_doublets[x], grilleCand);
							}
						}
					}
				}

			}

			// Colonnes
			cpt=0;
			doublets=new int[tailleGrille][identiques];
			for (int j=0; j<tailleGrille; j++){
				cpt_l=0;
				if (grilleCand[j][i][tailleGrille]==identiques){
					for (int x=0; x<tailleGrille; x++){
						if (grilleCand[j][i][x]>0){
							doublets[cpt][cpt_l]=grilleCand[j][i][x];
							cpt_l++;
						}
					}	
					cpt++;
				}

			}
			nb_doublets=cpt;
			cpt=0;
			// Identification doublets
			if (nb_doublets>=identiques){
				tmp_doublets=this.idIdentiques(doublets, identiques);
				if (tmp_doublets[0]!=-1){
					nbDoublets++;
					if (verbose) System.out.println("Doublet trouve : ("+tmp_doublets[0]+","+tmp_doublets[1]+")");
					// On a trouver un doublets
					for (int j=0;j<tailleGrille; j++){
						if (grilleCand[j][i][tailleGrille]>identiques){
							for (int x=0;x<identiques;x++){
								this.grilleCandUpdate(j, i, tmp_doublets[x], grilleCand);
							}
						}
					}
				}

			}			
		}

		// Block
		for (int i=0; i<tailleGrille; i=i+sep_l){
			for (int j=0; j<tailleGrille; j=j+sep_c){
				cpt=0;
				doublets=new int[tailleGrille][identiques];
				for (int x=i;x<i+sep_l;x++){
					for (int y=j; y<j+sep_c; y++){
						cpt_l=0;
						if (grilleCand[x][y][tailleGrille]==identiques){
							for (int u=0; u<tailleGrille; u++){
								if (grilleCand[x][y][u]>0){
									doublets[cpt][cpt_l]=grilleCand[x][y][u];
									cpt_l++;
								}
							}	
							cpt++;
						}
					}
				}
				nb_doublets=cpt;
				cpt=0;
				// Identification doublets
				if (nb_doublets>=identiques){
					tmp_doublets=this.idIdentiques(doublets, identiques);
					if (tmp_doublets[0]!=-1){
						nbDoublets++;
						if (verbose) System.out.println("Doublet trouve : ("+tmp_doublets[0]+","+tmp_doublets[1]+")");
						// On a trouver un doublets
						for (int x=i;x<i+sep_l;x++){
							for (int y=j;y<j+sep_c;y++){
								if (grilleCand[x][y][tailleGrille]>identiques){
									for (int u=0;u<identiques;u++){
										this.grilleCandUpdate(x, y, tmp_doublets[u], grilleCand);
									}
								}
							}
						}
					}
				}
			}	
		}

		return nbDoublets;
	}

	/**
	 * Identifie les pair dans un tableau
	 * 
	 * @param candidats le tableau a deux dimensions contenant les pair
	 * @param identiques pair, triples, etc.. non fonctionnel
	 * @return le doublet identifiee
	 */
	public int[] idIdentiques(int[][] candidats, int identiques){
		int[] id=new int[identiques];
		boolean[] doublets;
		boolean[] tmp_doublets = new boolean[tailleGrille];
		boolean ident=true;
		int cpt=0;
		id[0]=-1;

		for (int i=0; i<tailleGrille; i++){
			doublets = new boolean[tailleGrille];
			for (int j=0;j<identiques;j++){
				if (candidats[i][j]>0) doublets[candidats[i][j]-1]=true;
			}
			for (int j=i+1; j<tailleGrille; j++){
				tmp_doublets=new boolean[tailleGrille];
				for (int y=0; y<tailleGrille; y++){
					if (doublets[y]==true){
						tmp_doublets[y]=true;
					}
				}
				for (int x=0;x<identiques;x++){
					if (candidats[j][x]>0) tmp_doublets[candidats[j][x]-1]=false;
				}
				for (int y=0; y<tailleGrille; y++){
					if (tmp_doublets[y]==true){
						ident=false;
					}
				}
				if (ident){
					cpt=0;
					for (int x=0; x<tailleGrille; x++){
						if (doublets[x]==true){
							id[cpt]=x+1;
							cpt++;
						}
					}
					return id;	
				}
			}
		}
		return id;
	}

	/**
	 * Identifie les candidats uniques dans une grille de candidats
	 * 
	 * @param grilleCand
	 * @param verbose sortie complete
	 * @return le nombre de candidats uniques trouves
	 */
	public int candidatUnique(int[][][] grilleCand, boolean verbose){
		int nb_candidatsUniques=0;
		int[][] candidats_l;
		int[][] candidats_c;
		int[][] candidats_b;
		// Lignes et colonnes
		for (int i=0; i<tailleGrille; i++){
			candidats_l = new int[tailleGrille][3];
			candidats_c = new int[tailleGrille][3];
			// Lignes
			// On compte le nombre d'ocurrences des candidats
			for (int j=0; j<tailleGrille; j++){
				// Lignes
				if (grilleCand[i][j][tailleGrille]>0){
					for (int x=0; x<tailleGrille; x++){
						if (grilleCand[i][j][x]>0){
							candidats_l[grilleCand[i][j][x]-1][0]++;
							candidats_l[grilleCand[i][j][x]-1][1] = i;
							candidats_l[grilleCand[i][j][x]-1][2] = j;
						}
					}
				}
			}

			// On regarde si un des candidats est unique et on met a jour
			for (int x=0; x<tailleGrille; x++){
				if (candidats_l[x][0]==1){
					if (verbose) System.out.println("Candidat unique ligne trouve : ["+(candidats_l[x][1]+1)+","+(candidats_l[x][2]+1)+"] "+this.convertir(x+1));
					for (int y=0;y<tailleGrille;y++){
						if (grilleCand[candidats_l[x][1]][candidats_l[x][2]][y]==x+1){
							this.grilleCandUpdate(candidats_l[x][1], candidats_l[x][2], x+1, grilleCand);
						} else {
							grilleCand[candidats_l[x][1]][candidats_l[x][2]][y]=0;
						}
					}
					grilleCand[candidats_l[x][1]][candidats_l[x][2]][tailleGrille]=0;
					nb_candidatsUniques++;
				}
			}
		}
		for (int i=0; i<tailleGrille; i++){
			candidats_c = new int[tailleGrille][3];
			// Colonnes
			// On compte le nombre d'ocurrences des candidats
			for (int j=0; j<tailleGrille; j++){
				// Lignes
				if (grilleCand[j][i][tailleGrille]>0){
					for (int x=0; x<tailleGrille; x++){
						if (grilleCand[j][i][x]>0){
							candidats_c[grilleCand[j][i][x]-1][0]++;
							candidats_c[grilleCand[j][i][x]-1][1] = j;
							candidats_c[grilleCand[j][i][x]-1][2] = i;
						}
					}
				}
			}
			// On regarde si un des candidats est unique et on met a jour
			for (int x=0; x<tailleGrille; x++){
				if (candidats_c[x][0]==1){
					if (verbose) System.out.println("Candidat unique colonne trouve : ["+(candidats_c[x][1]+1)+","+(candidats_c[x][2]+1)+"] "+this.convertir(x+1));
					for (int y=0;y<tailleGrille;y++){
						if (grilleCand[candidats_c[x][1]][candidats_c[x][2]][y]==x+1){
							this.grilleCandUpdate(candidats_c[x][1], candidats_c[x][2], x+1, grilleCand);
						} else {
							grilleCand[candidats_c[x][1]][candidats_c[x][2]][y]=0;
						}
					}
					grilleCand[candidats_c[x][1]][candidats_c[x][2]][tailleGrille]=0;
					nb_candidatsUniques++;
				}
			}
		}

		// Blocks
		for (int i=0; i<tailleGrille; i=i+sep_l){
			for (int j=0; j<tailleGrille; j=j+sep_c){
				candidats_b = new int[tailleGrille][3];
				// Nombre d'ocurrences de candidats
				for (int x=i; x<i+sep_l; x++){
					for (int y=j; y<j+sep_c; y++){
						if (grilleCand[x][y][tailleGrille]>0){
							for (int u=0; u<tailleGrille; u++){
								if (grilleCand[x][y][u]>0){
									candidats_b[grilleCand[x][y][u]-1][0]++;
									candidats_b[grilleCand[x][y][u]-1][1] = x;
									candidats_b[grilleCand[x][y][u]-1][2] = y;
								}
							}
						}
					}
				}
				for (int x=0; x<tailleGrille; x++){
					if (candidats_b[x][0]==1){
						if (verbose) System.out.println("Candidat unique block trouve : ["+(candidats_b[x][1]+1)+","+(candidats_b[x][2]+1)+"] "+this.convertir(x+1));
						for (int y=0;y<tailleGrille;y++){
							if (grilleCand[candidats_b[x][1]][candidats_b[x][2]][y]==x+1){
								this.grilleCandUpdate(candidats_b[x][1], candidats_b[x][2], x+1, grilleCand);
							} else {
								grilleCand[candidats_b[x][1]][candidats_b[x][2]][y]=0;
							}
						}
						grilleCand[candidats_b[x][1]][candidats_b[x][2]][tailleGrille]=0;
						nb_candidatsUniques++;	
					}
				}
			}
		}
		return nb_candidatsUniques;
	}

	/**
	 * Capture de l'entree clavier en verifiant que les valeurs
	 * entrees sont bien dans l'ensemble de definition des 
	 * valeurs de la grille.
	 * 
	 * @return valeur entree au clavier et convertie au 
	 * format interne de la grille (-1=vide, A=10, B=11,etc.)
	 */
	public int capture(){
		int val;
		val = this.convertir(Deug.readString().toCharArray()[0]);
		while (val>tailleGrille || val==0 || val<-1){
			System.out.println("\nLa valeur n'est pas dans l'ensemble de definition.\nEntrer � nouveau une valeur.");
			val = this.convertir(Deug.readString().toCharArray()[0]);
		}
		return val;
	}

	/**
	 * Verifie si une grille est juste (ou solvable a priori),
	 * ie s'il n'y a aucune valeur qui entre en conflit avec une autre.
	 * 
	 * @param grille le tableau a verifier
	 * @return true si juste, false sinon
	 */
	public boolean estJuste(int[][] grille) {
		boolean[] val_c;
		boolean[] val_l;
		int d;
		boolean r = true;
		// Check pas deux fois la meme valeur
		for (int k = 0; k < tailleGrille && r; k++) {
			val_c = new boolean[tailleGrille];
			val_l = new boolean[tailleGrille];
			for (int l=0; l<tailleGrille && r; l++) {
				d = grille[k][l];
				if (d > 0 && !val_c[d-1]) {
					val_c[d-1] = true;
				} else if (d != -1) {
					r = false;
				}

				d = grille[l][k];
				if (d > 0 && !val_l[d-1]) {
					val_l[d-1] = true;
				} else if (d!=-1) {
					r = false;
				}

			}
		}
		int lig, col;
		for (int i=0; i<tailleGrille; i=i+sep_l) {
			for (int j=0; j<tailleGrille; j=j+sep_c) {
				lig = i/sep_l;
				col = j/sep_c;
				val_c = new boolean[tailleGrille];
				for (int z=lig*sep_l; z<lig*sep_l+sep_l; z++) {
					for (int h=col*sep_c; h<col*sep_c+sep_c; h++) {
						d = grille[z][h];
						if (d > 0 && !val_c[d-1]) {
							val_c[d - 1] = true;
						} else if (d != -1) {
							r = false;
						}
					}
				}
			}
		}
		if (r){
			// check au moins une valeur possible par case vide
			int[][][] grilleCandTmp = this.grilleCandidats(grille) ;
			for (int i=0; i<tailleGrille; i++) {
				for (int j=0; j<tailleGrille; j++){
					if (grille[i][j]==-1 && grilleCandTmp[i][j][tailleGrille]==0){
						r=false;
					}
				}
			}

			if (r){
				// check valeur possible au moins une fois
				val_l = new boolean[tailleGrille];
				val_c = new boolean[tailleGrille];
				for (int i=0; i<tailleGrille; i++){
					// Valeur presentes lignes/colonnes
					for (int j=0; j<tailleGrille; j++){
						if (grille[i][j]!=-1){
							val_l[grille[i][j]-1]=true;
						}
						if (grille[j][i]!=-1){
							val_c[grille[j][i]-1]=true;
						}
					}
					for (int j=0;j<tailleGrille; j++){
						for (int x=0; x<tailleGrille; x++){
							if (grilleCandTmp[i][j][x]!=0){
								val_l[grilleCandTmp[i][j][x]-1] = true;
							}
							if (grilleCandTmp[j][i][x]!=0){
								val_c[grilleCandTmp[j][i][x]-1] = true;
							}
						}
					}
					for (int x=0; x<tailleGrille; x++){
						if (!val_l[x]){
							r=false;
						}
						if (!val_c[x]){
							r=false;
						}
					}
				}
			}
		}
		return r;
	}

	/**
	 * Remplit une grille case par case.
	 */
	public void remplir() {
		for (int i=0; i<tailleGrille; i++) {
			for (int j=0; j<tailleGrille; j++) {
				if (grille[i][j]==-1){
					this.afficher(grille);
					System.out.print("\nEntrer le nombre en position ["+(i+1)+","+(j+1)+"] : \n");
					grille[i][j] = this.capture();
					System.out.println();
				}
			}
		}
		this.afficher(grille);
		if (this.estJuste(grille)){
			System.out.println("La grille est juste.");
		} else {
			System.out.println("La grille est fausse.");
		}
	}

	/**
	 * Charge une grille deja enregistree dans un fichier
	 * 
	 * @param fichier nom du fichier a charger (avec son chemin
	 * pour ne pas utiliser le repertoire courant)
	 * @param grilleCourante indique si on definit la grille chargee
	 * en temps que grille courante
	 * @return la grille chargee
	 */
//	public int[][] charger(String fichier, boolean grilleCourante) throws ExceptionsSudoku, IOException {
//		int zl, zc;
//		int [][] data;
//		Reader r = new FileReader(fichier);
//		Scanner s = new Scanner(r);
//		try {
//			if (s.hasNextInt()) zl = s.nextInt();
//			else throw new ExceptionsSudoku("format de la grille incorrect");
//			if (s.hasNextInt()) zc = s.nextInt();
//			else throw new ExceptionsSudoku("format de la grille incorrect");
//			data = new int[zl*zc][zl*zc];
//			for (int l = 0; l < zl*zc; l++) {
//				for (int c = 0; c < zl*zc; c++) {
//					if (s.hasNextInt()){
//						data[l][c] = s.nextInt()+1;
//						if (data[l][c]>zl*zc){
//							throw new ExceptionsSudoku("valeur non comprise dans l'ensemble de definition");
//						}
//					} else if (s.hasNext() && s.next().equals("?")) {
//						data[l][c] = -1;
//					} else { 
//						throw new ExceptionsSudoku("format de la grille incorrect");
//					}
//				}
//			}
//			for (int l = 0; l < zl*zc; l++) {
//				for (int c = 0; c < zl*zc; c++) {
//					if (s.hasNextInt()){
//						positionEnoncer[l][c] = (s.nextInt() != 0);
//					} else { 
//						throw new ExceptionsSudoku("format de la grille incorrect");
//					}
//				}
//			}
//			if (s.hasNext()) {
//				String[] str = s.next().split(":");
//				
//				if ((new Character(str[3].charAt(0))).equals('-') || str[3].length()>3){
//					chrono.setTimer(new Integer(str[0]).intValue(), new Integer(str[1]).intValue(), new Integer(str[2]).intValue(), 0);
//
//				} else {
//					chrono.setTimer(new Integer(str[0]).intValue(), new Integer(str[1]).intValue(), new Integer(str[2]).intValue(), new Integer(str[3]).intValue());
//
//				}
//			} else { 
//				throw new ExceptionsSudoku("format de fichier incorrect");
//			}
//		}
//		finally {
//			s.close();
//		}
//		if (grilleCourante){
//			this.setGrilleCourante(data);
//		}
//		return data;
//	}

	/**
	 * Format la grille pour son enregistrement
	 * @param f le formatter
	 */
//	public void output(Formatter f) {
//		for (int l = 0; l < sep_l*sep_c; l++) {
//			if (l % sep_l == 0) f.format("%n");
//			for (int c = 0; c < sep_l*sep_c; c++) {
//				if (c % sep_c == 0) f.format(" ");
//				if (grille[l][c] == -1)
//					f.format("%2s ", "?");
//				else
//					f.format("%2d ",grille[l][c]-1);
//			}
//			f.format("%n");
//		}
//		f.format("%n");
//		for (int l = 0; l < sep_l*sep_c; l++) {
//			if (l % sep_l == 0) f.format("%n");
//			for (int c = 0; c < sep_l*sep_c; c++) {
//				if (c % sep_c == 0) f.format(" ");
//				if (positionEnoncer[l][c]) {
//					f.format("%2s ", "1");
//				} else {
//					f.format("%2s ", "0");
//				}				
//			}
//			f.format("%n");
//		}
//		f.format("%n");
//		f.format("%2s ", chrono.getTimer());
//		f.format("%n");
//	}

	/**
	 * Enregistre une grille dans un fichier.
	 * 
	 * @param grille le tableau a enregistrer
	 * @param fichier le fichier dans lequel l'enregistrer
	 * @return true si OK, false sinon
	 */
//	public boolean enregistrer(int[][] grille, String fichier) throws IOException {
//		Writer w = new FileWriter(fichier);
//		Formatter f = new Formatter(w);
//		try {
//			f.format("%d %d%n", sep_l, sep_c);
//			output(f);
//		}
//		finally {
//			f.close();
//		}
//		return true;
//	}

	/**
	 * Definit une grille comme la grille courante
	 * @param grille la grille a definir comme grille courante
	 */
	public void setGrilleCourante(int[][] gril){
		grille = gril;
		tailleGrille = grille.length;
		setTailleBlock();
		int[][][] solutionTmp = resoudre(grille, explicite, false);
		solution = solutionTmp[1];
		nbSolutions = solutionTmp[0][0][0];
		difficulte = evaluerDifficulte(grille, verbose);
		nbCasesVides = nbCasesVidesTmp;
		nbCasesImm = nbCasesImmTmp;
		nbCandUniques = nbCandUniquesTmp;
		nbDoublets = nbDoubletsTmp;
	}

	/**
	 * Active/Desactive le mode verbose
	 */
	public void toggleVerbose(){
		verbose = !verbose;
	}

}
